标签
spring
字数
1574 字
阅读时间
10 分钟
一、通过spring类重载
1.1 注解
java
import java.lang.annotation.ElementType;
import java.lang.annotation.Retention;
import java.lang.annotation.RetentionPolicy;
import java.lang.annotation.Target;
/**
* 覆盖父类组件
* @author sunwen
* @since 2020/8/19
*/
@Target({ElementType.TYPE})
@Retention(RetentionPolicy.RUNTIME)
public @interface OverrideSuperComponent {
}java
import com.commnetsoft.core.CoreConstant;
import com.commnetsoft.core.annotation.OverrideSuperComponent;
import com.commnetsoft.core.utils.ClassScaner;
import org.springframework.context.ResourceLoaderAware;
import org.springframework.core.io.ResourceLoader;
import org.springframework.core.type.ClassMetadata;
import org.springframework.core.type.classreading.MetadataReader;
import org.springframework.core.type.classreading.MetadataReaderFactory;
import org.springframework.core.type.filter.TypeFilter;
import org.springframework.stereotype.Component;
import java.io.IOException;
import java.util.List;
/**
* 二次开发Component重写覆盖过滤
* @author sunwen
* @since 2020/8/19
*/
public class ComponentOverridedFilter implements TypeFilter, ResourceLoaderAware {
private ResourceLoader resourceLoader;
private volatile List<ClassMetadata> overrideComponents;
@Override
public boolean match(MetadataReader metadataReader, MetadataReaderFactory metadataReaderFactory) throws IOException {
String className = metadataReader.getClassMetadata().getClassName();
if(metadataReader.getAnnotationMetadata().hasMetaAnnotation(Component.class.getName()) ||
metadataReader.getAnnotationMetadata().hasAnnotation(Component.class.getName())){
for(ClassMetadata c : getOverrideComponents()){
String superClassName = c.getSuperClassName();
if(superClassName != null && superClassName.equals(className)){
return true;
}
}
}
return false;
}
@Override
public void setResourceLoader( ResourceLoader resourceLoader) {
this.resourceLoader = resourceLoader;
}
protected List<ClassMetadata> getOverrideComponents() throws IOException {
if (this.overrideComponents == null) {
overrideComponents = ClassScaner.scanAnnotation(this.resourceLoader, CoreConstant.Package.BASE, OverrideSuperComponent.class);
}
return this.overrideComponents;
}
}二次开发需要满足替换类为目标类的子类,并添加注解作为标识
二、Controller请求地址处理方法重写
java
/**
* 为了支持Controller二次开发。允许继承的实现类 里面重复发布相同的url。
* 发布相同的url以子类为准。
* @author chenli
* @date 2017-5-25
* @version 1.0
*/
@Configuration
public class RequestMappingHandlerMapping extends RequestMappingInfoHandlerMapping
implements EmbeddedValueResolverAware {
private static final String CONTROLLER_NAME_SUFFIX = "Controller";
private static final String SERVLET_NAME_SUFFIX = "Servlet";
private static final String SCOPED_TARGET_NAME_PREFIX = "scopedTarget.";
private boolean useSuffixPatternMatch = true;
private boolean useRegisteredSuffixPatternMatch = false;
private boolean useTrailingSlashMatch = true;
private ContentNegotiationManager contentNegotiationManager = new ContentNegotiationManager();
private final List<String> fileExtensions = new ArrayList<String>();
private StringValueResolver embeddedValueResolver;
/**
* 为了支持二次开发。允许继承的实现类 里面重复发布相同的url。
* 发布相同的url以子类为准
*/
@Override
protected void initHandlerMethods() {
if (logger.isDebugEnabled()) {
logger.debug("Looking for request mappings in application context: " + getApplicationContext());
}
String[] beanNames = getApplicationContext().getBeanNamesForType(Object.class);
//
List<String> extend_beanNames = new ArrayList<String>();
for (String beanName : beanNames) {
if(beanName.endsWith(CONTROLLER_NAME_SUFFIX) || beanName.endsWith(SERVLET_NAME_SUFFIX)){
Object o = getApplicationContext().getBean(beanName);
Class c = o.getClass();
Class super_c = c.getSuperclass();
if(super_c.getSimpleName().endsWith(CONTROLLER_NAME_SUFFIX) || beanName.endsWith(SERVLET_NAME_SUFFIX)){
try {
RequestMapping mapping = o.getClass().getAnnotation(RequestMapping.class);
String[] temp_value = mapping.value();
mapping = o.getClass().getSuperclass().getAnnotation(RequestMapping.class);
if(mapping != null){
String[] temp_super_value = mapping.value();
if(ArrayUtils.isEquals(temp_value, temp_super_value)){
extend_beanNames.add(StrHelper.uncapitalize(super_c.getSimpleName()));
}
}
} catch (Exception e) {
logger.error("",e);
}
}
}
}
for (String beanName : beanNames) {
if (!beanName.startsWith(SCOPED_TARGET_NAME_PREFIX) &&
isHandler(getApplicationContext().getType(beanName)) && extend_beanNames.contains(beanName) == false){
detectHandlerMethods(beanName);
}
}
handlerMethodsInitialized(getHandlerMethods());
}
/**
* Whether to use suffix pattern match (".*") when matching patterns to
* requests. If enabled a method mapped to "/users" also matches to "/users.*".
* <p>The default value is {@code true}.
* <p>Also see {@link #setUseRegisteredSuffixPatternMatch(boolean)} for
* more fine-grained control over specific suffixes to allow.
*/
public void setUseSuffixPatternMatch(boolean useSuffixPatternMatch) {
this.useSuffixPatternMatch = useSuffixPatternMatch;
}
/**
* Whether to use suffix pattern match for registered file extensions only
* when matching patterns to requests.
* <p>If enabled, a controller method mapped to "/users" also matches to
* "/users.json" assuming ".json" is a file extension registered with the
* provided {@link #setContentNegotiationManager(ContentNegotiationManager)
* contentNegotiationManager}. This can be useful for allowing only specific
* URL extensions to be used as well as in cases where a "." in the URL path
* can lead to ambiguous interpretation of path variable content, (e.g. given
* "/users/{user}" and incoming URLs such as "/users/john.j.joe" and
* "/users/john.j.joe.json").
* <p>If enabled, this flag also enables
* {@link #setUseSuffixPatternMatch(boolean) useSuffixPatternMatch}. The
* default value is {@code false}.
*/
public void setUseRegisteredSuffixPatternMatch(boolean useRegisteredSuffixPatternMatch) {
this.useRegisteredSuffixPatternMatch = useRegisteredSuffixPatternMatch;
this.useSuffixPatternMatch = (useRegisteredSuffixPatternMatch || this.useSuffixPatternMatch);
}
/**
* Whether to match to URLs irrespective of the presence of a trailing slash.
* If enabled a method mapped to "/users" also matches to "/users/".
* <p>The default value is {@code true}.
*/
public void setUseTrailingSlashMatch(boolean useTrailingSlashMatch) {
this.useTrailingSlashMatch = useTrailingSlashMatch;
}
/**
* Set the {@link ContentNegotiationManager} to use to determine requested media types.
* If not set, the default constructor is used.
*/
public void setContentNegotiationManager(ContentNegotiationManager contentNegotiationManager) {
Assert.notNull(contentNegotiationManager, "ContentNegotiationManager must not be null");
this.contentNegotiationManager = contentNegotiationManager;
}
@Override
public void setEmbeddedValueResolver(StringValueResolver resolver) {
this.embeddedValueResolver = resolver;
}
@Override
public void afterPropertiesSet() {
if (this.useRegisteredSuffixPatternMatch) {
this.fileExtensions.addAll(this.contentNegotiationManager.getAllFileExtensions());
}
super.afterPropertiesSet();
}
/**
* Whether to use suffix pattern matching.
*/
public boolean useSuffixPatternMatch() {
return this.useSuffixPatternMatch;
}
/**
* Whether to use registered suffixes for pattern matching.
*/
public boolean useRegisteredSuffixPatternMatch() {
return this.useRegisteredSuffixPatternMatch;
}
/**
* Whether to match to URLs irrespective of the presence of a trailing slash.
*/
public boolean useTrailingSlashMatch() {
return this.useTrailingSlashMatch;
}
/**
* Return the configured {@link ContentNegotiationManager}.
*/
public ContentNegotiationManager getContentNegotiationManager() {
return this.contentNegotiationManager;
}
/**
* Return the file extensions to use for suffix pattern matching.
*/
public List<String> getFileExtensions() {
return this.fileExtensions;
}
/**
* {@inheritDoc}
* Expects a handler to have a type-level @{@link Controller} annotation.
*/
@Override
protected boolean isHandler(Class<?> beanType) {
return ((AnnotationUtils.findAnnotation(beanType, Controller.class) != null) ||
(AnnotationUtils.findAnnotation(beanType, RequestMapping.class) != null));
}
/**
* Uses method and type-level @{@link RequestMapping} annotations to create
* the RequestMappingInfo.
* @return the created RequestMappingInfo, or {@code null} if the method
* does not have a {@code @RequestMapping} annotation.
* @see #getCustomMethodCondition(Method)
* @see #getCustomTypeCondition(Class)
*/
@Override
protected RequestMappingInfo getMappingForMethod(Method method, Class<?> handlerType) {
RequestMappingInfo info = null;
RequestMapping methodAnnotation = AnnotationUtils.findAnnotation(method, RequestMapping.class);
if (methodAnnotation != null) {
RequestCondition<?> methodCondition = getCustomMethodCondition(method);
info = createRequestMappingInfo(methodAnnotation, methodCondition);
RequestMapping typeAnnotation = AnnotationUtils.findAnnotation(handlerType, RequestMapping.class);
if (typeAnnotation != null) {
RequestCondition<?> typeCondition = getCustomTypeCondition(handlerType);
info = createRequestMappingInfo(typeAnnotation, typeCondition).combine(info);
}
}
return info;
}
/**
* Provide a custom type-level request condition.
* The custom {@link RequestCondition} can be of any type so long as the
* same condition type is returned from all calls to this method in order
* to ensure custom request conditions can be combined and compared.
* <p>Consider extending {@link AbstractRequestCondition} for custom
* condition types and using {@link CompositeRequestCondition} to provide
* multiple custom conditions.
* @param handlerType the handler type for which to create the condition
* @return the condition, or {@code null}
*/
protected RequestCondition<?> getCustomTypeCondition(Class<?> handlerType) {
return null;
}
/**
* Provide a custom method-level request condition.
* The custom {@link RequestCondition} can be of any type so long as the
* same condition type is returned from all calls to this method in order
* to ensure custom request conditions can be combined and compared.
* <p>Consider extending {@link AbstractRequestCondition} for custom
* condition types and using {@link CompositeRequestCondition} to provide
* multiple custom conditions.
* @param method the handler method for which to create the condition
* @return the condition, or {@code null}
*/
protected RequestCondition<?> getCustomMethodCondition(Method method) {
return null;
}
/**
* Created a RequestMappingInfo from a RequestMapping annotation.
*/
protected RequestMappingInfo createRequestMappingInfo(RequestMapping annotation, RequestCondition<?> customCondition) {
String[] patterns = resolveEmbeddedValuesInPatterns(annotation.value());
return new RequestMappingInfo(
new PatternsRequestCondition(patterns, getUrlPathHelper(), getPathMatcher(),
this.useSuffixPatternMatch, this.useTrailingSlashMatch, this.fileExtensions),
new RequestMethodsRequestCondition(annotation.method()),
new ParamsRequestCondition(annotation.params()),
new HeadersRequestCondition(annotation.headers()),
new ConsumesRequestCondition(annotation.consumes(), annotation.headers()),
new ProducesRequestCondition(annotation.produces(), annotation.headers(), this.contentNegotiationManager),
customCondition);
}
/**
* Resolve placeholder values in the given array of patterns.
* @return a new array with updated patterns
*/
protected String[] resolveEmbeddedValuesInPatterns(String[] patterns) {
if (this.embeddedValueResolver == null) {
return patterns;
}
else {
String[] resolvedPatterns = new String[patterns.length];
for (int i = 0; i < patterns.length; i++) {
resolvedPatterns[i] = this.embeddedValueResolver.resolveStringValue(patterns[i]);
}
return resolvedPatterns;
}
}
}三、请求返回页面重载
java
/**
* 页面二次化页面开发自动加载插件。
* 如果存在个性化页面,则优先使用个性化页面。
* @author chenli
* @date:2015-2-9 下午4:24:59
* @version :4.2
*
*/
public class PluginInternalResourceViewResolver extends InternalResourceViewResolver{
private Logger logger = LoggerFactory.getLogger(PluginInternalResourceViewResolver.class);
@Override
protected String getPrefix() {
return super.getPrefix();
}
@Override
protected AbstractUrlBasedView buildView(String viewName) throws Exception {
InternalResourceView view = (InternalResourceView) super.buildView(viewName);
/**
* 判断文件是否存在,如果不存在返回null,存在才返回view
* 原InternalResourceViewResolver不管是否存在都会返回一个view
* 如果采用视图链方式,判断是否为空,如果为空才查询下一个视图配置。
* 所以重写方式,以适用插件式开发
*/
String viewUrl = view.getUrl();
if(viewUrl != null){
if(isExist(viewUrl) == false){//插件路径不存在
view = null;
}
}
return view;
}
/**
*
* @param viewUrl
* @return
* @author chenli
* @data 2015-3-3
*/
private boolean isExist(String viewUrl){
boolean exist = false;
//判断地址是否存在
try {
File f = getApplicationContext().getResource(viewUrl).getFile();
exist = f.exists();
} catch (IllegalStateException e) {
// 异常输出 不必要输出
} catch (IOException e) {
// 异常输出
}
return exist;
}
@Override
protected View loadView(String viewName, Locale locale) throws Exception {
AbstractUrlBasedView view = buildView(viewName);
if(view == null){
return null;
}
View result = applyLifecycleMethods(viewName, view);
return (view.checkResource(locale) ? result : null);
}
private View applyLifecycleMethods(String viewName, AbstractView view) {
return (View) getApplicationContext().getAutowireCapableBeanFactory().initializeBean(view, viewName);
}
}